/*
* Copyright (c) 2017 Sprint
*
* Licensed under the Apache License, Version 2.0 (the "License");
* you may not use this file except in compliance with the License.
* You may obtain a copy of the License at
*
*    http://www.apache.org/licenses/LICENSE-2.0
*
* Unless required by applicable law or agreed to in writing, software
* distributed under the License is distributed on an "AS IS" BASIS,
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
* See the License for the specific language governing permissions and
* limitations under the License.
*/

#ifndef __EPC_H
#define __EPC_H

#include <stdio.h>

#include <algorithm>
#include <string>
#include <sstream>
#include <list>
#include <vector>


/*
   MCC digit 1 - low order nibble octet 1
   MCC digit 2 - high order nibble octet 1
   MCC digit 3 - low order nibble octet 2

   MCC = [MCC digit 1][MCC digit 2][MCC digit 3]


   MNC digit 1 - low order nibble octet 3
   MNC digit 2 - high order nibble octet 3
   MNC digit 3 - high order nibble octet 2, nibble set to 1111 if MNC len is 2

   MNC = [MNC digit 1][MNC digit 2][MNC digit 3]
*/

#define LOW_NIBBLE(a) (((unsigned char)a) & 0x0f)
#define HIGH_NIBBLE(a) ((((unsigned char)a) & 0xf0) >> 4)

#define PARSE_MNC(a,b) { \
   if ( HIGH_NIBBLE(b[1]) == 0x0f ) { \
      a[0] = '0'; \
      a[1] = LOW_NIBBLE(b[2]) + '0'; \
      a[2] = HIGH_NIBBLE(b[2]) + '0'; \
      a[3] = '\0'; \
   } else { \
      a[0] = LOW_NIBBLE(b[2]) + '0'; \
      a[1] = HIGH_NIBBLE(b[2]) + '0'; \
      a[2] = HIGH_NIBBLE(b[1]) + '0'; \
      a[3] = '\0'; \
   } \
}

#define PARSE_MCC(a,b) { \
   a[0] = LOW_NIBBLE(b[0]) + '0';  \
   a[1] = HIGH_NIBBLE(b[0]) + '0'; \
   a[2] = LOW_NIBBLE(b[1]) + '0'; \
   a[3] = '\0'; \
}
   
#define PARSE_PLMNID(a) \
   char mnc[4], mcc[4]; \
   PARSE_MNC(mnc,a); \
   PARSE_MCC(mcc,a);

#define APPEND_MNC(a) append("mnc").append(a).append(".")
#define APPEND_MCC(a) append("mcc").append(a).append(".")
#define APPEND_3GPPNETWORK append("3gppnetwork.org")

namespace EPC
{
   enum AppServiceEnum
   {
      x_3gpp_unknown,
      x_3gpp_pgw,
      x_3gpp_sgw,
      x_3gpp_ggsn,
      x_3gpp_sgsn,
      x_3gpp_mme,
      x_3gpp_msc
   };
   
   enum AppProtocolEnum
   {
      x_unknown,
      x_gn,
      x_gp,
      x_nq,
      x_nqprime,
      x_s10,
      x_s11,
      x_s12,
      x_s1_mme,
      x_s1_u,
      x_s16,
      x_s2a_gtp,
      x_s2a_mipv4,
      x_s2a_pmip,
      x_s2b_gtp,
      x_s2b_pmip,
      x_s2c_dsmip,
      x_s3,
      x_s4,
      x_s5_gtp,
      x_s5_pmip,
      x_s6a,
      x_s8_gtp,
      x_s8_pmip,
      x_sv
   };
   
   enum PGWAppProtocolEnum
   {
      pgw_x_gn,
      pgw_x_gp,
      pgw_x_s2a_gtp,
      pgw_x_s2a_mipv4,
      pgw_x_s2a_pmip,
      pgw_x_s2b_gtp,
      pgw_x_s2b_pmip,
      pgw_x_s2c_dsmip,
      pgw_x_s5_gtp,
      pgw_x_s5_pmip,
      pgw_x_s8_gtp,
      pgw_x_s8_pmip
   };
   
   enum SGWAppProtocolEnum
   {
      sgw_x_s11,
      sgw_x_s12,
      sgw_x_s1_u,
      sgw_x_s2a_pmip,
      sgw_x_s2b_pmip,
      sgw_x_s4,
      sgw_x_s5_gtp,
      sgw_x_s5_pmip,
      sgw_x_s8_gtp,
      sgw_x_s8_pmip
   };
   
   enum GGSNAppProtocolEnum
   {
      ggsn_x_gn,
      ggsn_x_gp
   };
   
   enum SGSNAppProtocolEnum
   {
      sgsn_x_gn,
      sgsn_x_gp,
      sgsn_x_nqprime,
      sgsn_x_s16,
      sgsn_x_s3,
      sgsn_x_s4,
      sgsn_x_sv
   };
   
   enum MMEAppProtocolEnum
   {
      mme_x_gn,
      mme_x_gp,
      mme_x_nq,
      mme_x_s10,
      mme_x_s11,
      mme_x_s1_mme,
      mme_x_s3,
      mme_x_s6a,
      mme_x_sv
   };
   
   enum MSCAppProtocolEnum
   {
      msc_x_sv
   };
   
   enum DiameterApplicationEnum
   {
      dia_app_unknown,
      /*
      +------------------+----------------------------+
      | Tag              | Diameter Application       |
      +------------------+----------------------------+
      | aaa+ap1          | NASREQ [RFC3588]           |
      | aaa+ap2          | Mobile IPv4 [RFC4004]      |
      | aaa+ap3          | Base Accounting [RFC3588]  |
      | aaa+ap4          | Credit Control [RFC4006]   |
      | aaa+ap5          | EAP [RFC4072]              |
      | aaa+ap6          | SIP [RFC4740]              |
      | aaa+ap7          | Mobile IPv6 IKE [RFC5778]  |
      | aaa+ap8          | Mobile IPv6 Auth [RFC5778] |
      | aaa+ap9          | QoS [RFC5866]              |
      | aaa+ap4294967295 | Relay [RFC3588]            |
      +------------------+----------------------------+
      */
      dia_app_nasreq,
      dia_app_mobile_ipv4,
      dia_app_base_accounting,
      dia_app_credit_control,
      dia_app_eap,
      dia_app_sip6,
      dia_app_mobile_ipv6_ike,
      dia_app_mobile_ipv6_auth,
      dia_app_qos,
      dia_app_relay,
      /*
      +----------------+----------------------+
      | Tag            | Diameter Application |
      +----------------+----------------------+
      | aaa+ap16777250 | 3GPP STa [TS29.273]  |
      | aaa+ap16777251 | 3GPP S6a [TS29.272]  |
      | aaa+ap16777264 | 3GPP SWm [TS29.273]  |
      | aaa+ap16777267 | 3GPP S9 [TS29.215]   |
      +----------------+----------------------+
      */
      dia_app_3gpp_sta,
      dia_app_3gpp_s6a,
      dia_app_3gpp_swm,
      dia_app_3gpp_s9,
      /*
      +----------------+--------------------------------------------------+
      | Tag            | Diameter Application                             |
      +----------------+--------------------------------------------------+
      | aaa+ap16777281 | WiMAX Network Access Authentication and          |
      |                | Authorization Diameter Application (WNAAADA)     |
      |                | [WiMAX-BASE]                                     |
      | aaa+ap16777282 | WiMAX Network Accounting Diameter Application    |
      |                | (WNADA) [WiMAX-BASE]                             |
      | aaa+ap16777283 | WiMAX MIP4 Diameter Application (WM4DA)          |
      |                | [WiMAX-BASE]                                     |
      | aaa+ap16777284 | WiMAX MIP6 Diameter Application (WM6DA)          |
      |                | [WiMAX-BASE]                                     |
      | aaa+ap16777285 | WiMAX DHCP Diameter Application (WDDA)           |
      |                | [WiMAX-BASE]                                     |
      | aaa+ap16777286 | WiMAX Location Authentication Authorization      |
      |                | Diameter Application (WLAADA) [WiMAX-LBS]        |
      | aaa+ap16777287 | WiMAX Policy and Charging Control R3 Policies    |
      |                | Diameter Application (WiMAX PCC-R3-P)            |
      |                | [WiMAX-PCC]                                      |
      | aaa+ap16777288 | WiMAX Policy and Charging Control R3 Offline     |
      |                | Charging Diameter Application (WiMAX PCC-R3-OFC) |
      |                | [WiMAX-PCC]                                      |
      | aaa+ap16777289 | WiMAX Policy and Charging Control R3 Offline     |
      |                | Charging Prime Diameter Application (WiMAX       |
      |                | PCC-R3-OFC-PRIME) [WiMAX-PCC]                    |
      | aaa+ap16777290 | WiMAX Policy and Charging Control R3 Online      |
      |                | Charging Diameter Application (WiMAX PCC-R3-OC)  |
      |                | [WiMAX-PCC]                                      |
      +----------------+--------------------------------------------------+
      */
      dia_app_wimax_wnaaada,
      dia_app_wimax_wnada,
      dia_app_wimax_wm4da,
      dia_app_wimax_wm6da,
      dia_app_wimax_wdda,
      dia_app_wimax_wlaada,
      dia_app_wimax_pcc_r3_p,
      dia_app_wimax_pcc_r3_ofc,
      dia_app_wimax_pcc_r3_ofc_prime,
      dia_app_wimax_pcc_r3_oc
   };

   enum DiameterProtocolEnum
   {
      dia_protocol_unknown,
      dia_protocol_tcp,
      dia_protocol_sctp,
      dia_protocol_tls_tcp
   };

   class Utility
   {
   public:
      static std::string home_network( const char *mnc, const char *mcc );
      static std::string home_network( const unsigned char *plmnid );
      static std::string home_network_gprs( const char *mnc, const char *mcc );
      static std::string home_network_gprs( const unsigned char *plmnid );
      static std::string tai_fqdn( const char *lb, const char *hb, const char *mnc, const char *mcc );
      static std::string tai_fqdn( const char *lb, const char *hb, const unsigned char *plmnid );
      static std::string mme_fqdn( const char *mmec, const char *mmegi, const char *mnc, const char *mcc );
      static std::string mme_fqdn( const char *mmec, const char *mmegi, const unsigned char *plmnid );
      static std::string mme_pool_fqdn( const char *mmegi, const char *mnc, const char *mcc );
      static std::string mme_pool_fqdn( const char *mmegi, const unsigned char *plmnid );
      static std::string rai_fqdn( const char *rac, const char *lac, const char *mnc, const char *mcc );
      static std::string rai_fqdn( const char *rac, const char *lac, const unsigned char *plmnid );
      static std::string rnc_fqdn( const char *rnc, const char *mnc, const char *mcc );
      static std::string rnc_fqdn( const char *rnc, const unsigned char *plmnid );
      static std::string sgsn_fqdn( const char *nri, const char *rac, const char *lac, const char *mnc, const char *mcc );
      static std::string sgsn_fqdn( const char *nri, const char *rac, const char *lac, const unsigned char *plmnid );
      static std::string epc_nodes_domain_fqdn( const char *mnc, const char *mcc );
      static std::string epc_nodes_domain_fqdn( const unsigned char *plmnid );
      static std::string epc_node_fqdn( const char *node, const char *mnc, const char *mcc );
      static std::string epc_node_fqdn( const char *node, const unsigned char *plmnid );
      static std::string nonemergency_epdg_oi_fqdn( const char *mnc, const char *mcc );
      static std::string nonemergency_epdg_oi_fqdn( const unsigned char *plmnid );
      static std::string nonemergency_epdg_tai_fqdn( const char *lb, const char *hb, const char *mnc, const char *mcc );
      static std::string nonemergency_epdg_tai_fqdn( const char *lb, const char *hb, const unsigned char *plmnid );
      static std::string nonemergency_epdg_lac_fqdn( const char *lac, const char *mnc, const char *mcc );
      static std::string nonemergency_epdg_lac_fqdn( const char *lac, const unsigned char *plmnid );
      static std::string nonemergency_epdg_visitedcountry_fqdn( const char *mcc );
      static std::string nonemergency_epdg_visitedcountry_fqdn( const unsigned char *plmnid );
      static std::string emergency_epdg_oi_fqdn( const char *mnc, const char *mcc );
      static std::string emergency_epdg_oi_fqdn( const unsigned char *plmnid );
      static std::string emergency_epdg_tai_fqdn( const char *lb, const char *hb, const char *mnc, const char *mcc );
      static std::string emergency_epdg_tai_fqdn( const char *lb, const char *hb, const unsigned char *plmnid );
      static std::string emergency_epdg_lac_fqdn( const char *lac, const char *mnc, const char *mcc );
      static std::string emergency_epdg_lac_fqdn( const char *lac, const unsigned char *plmnid );
      static std::string emergency_epdg_visitedcountry_fqdn( const char *mcc );
      static std::string emergency_epdg_visitedcountry_fqdn( const unsigned char *plmnid );
      static std::string global_enodeb_id_fqdn( const char *enb, const char *mcc );
      static std::string global_enodeb_id_fqdn( const char *enb, const unsigned char *plmnid );
      static std::string local_homenetwork_fqdn( const char *lhn, const char *mcc );
      static std::string local_homenetwork_fqdn( const char *lhn, const unsigned char *plmnid );
      static std::string epc( const char *mnc, const char *mcc );
      static std::string epc( const unsigned char *plmnid );
      static std::string apn_fqdn( const char *apnoi, const char *mnc, const char *mcc );
      static std::string apn_fqdn( const char *apnoi, const unsigned char *plmnid );
      static std::string apn( const char *apnoi, const char *mnc, const char *mcc );
      static std::string apn( const char *apnoi, const unsigned char *plmnid );
      static std::string apn_label(const std::string& apn);
   
      static AppServiceEnum getAppService( const std::string &s );
      static AppProtocolEnum getAppProtocol( const std::string &p );

      static const char *getAppService( AppServiceEnum s );
      static const char *getAppProtocol( AppProtocolEnum proto );
   
      static AppProtocolEnum getAppProtocol( PGWAppProtocolEnum proto );
      static AppProtocolEnum getAppProtocol( SGWAppProtocolEnum proto );
      static AppProtocolEnum getAppProtocol( GGSNAppProtocolEnum proto );
      static AppProtocolEnum getAppProtocol( SGSNAppProtocolEnum proto );
      static AppProtocolEnum getAppProtocol( MMEAppProtocolEnum proto );
      static AppProtocolEnum getAppProtocol( MSCAppProtocolEnum proto );
   
      static std::string diameter_fqdn( const char *mnc, const char *mcc );
      static std::string diameter_fqdn( const unsigned char *plmnid );

      static uint32_t getDiameterApplication( DiameterApplicationEnum app );
      static const char *getDiameterProtocol( DiameterProtocolEnum protocol );

      static std::string getDiameterService( DiameterApplicationEnum app, DiameterProtocolEnum protocol );

   private:
      Utility() {}
   };
   


} // namespace EPC

#endif // #ifndef __EPC_H
